﻿using Sustainalytics.Claims;
using Sustainalytics.DataLayer;
using Sustainalytics.Entities;
using Sustainalytics.ScreeningTool.BL;
using Sustainalytics.ScreeningTool.BL.Exceptions;
using Sustainalytics.ScreeningTool.BL.Model;
using Sustainalytics.ScreeningTool.Service.Utility;
using Sustainalytics.Utils;
using Sustainalytics.Utils.Serilog;
using Sustainalytics.Utils.WebApi.ErrorCodes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;
using WebApi.OutputCache.V2;

namespace Sustainalytics.ScreeningTool.Service
{
    [EnableCors(origins: "*", headers: "*", methods: "*")]
    [RoutePrefix("api/v1/filters")]
    [ClaimsChecker("api/v1/filters")]
    public class FiltersController : ApiController, IFiltersController
    {
        private readonly IFilterManager _filterManager;
        private readonly IAccountsRepository _accountsRepository;
        private readonly IClaimsChecker _claimsChecker;
        private readonly ICompanyScreeningRepository _companyScreeningRepository;

        public FiltersController(
            IFilterManager filterManager,
            IClaimsChecker claimsChecker,
            IAccountsRepository accountsRepository,
            ICompanyScreeningRepository companyScreeningRepository)
        {
            try
            {
                _filterManager = filterManager;
                _claimsChecker = claimsChecker;
                _accountsRepository = accountsRepository;
                _companyScreeningRepository = companyScreeningRepository;

            }
            catch (Exception ex)
            {
                throw new HttpResponseException(HttpStatusCode.InternalServerError);
            }
        }

        [Route("adminfilters/{accountId}")]
        public HttpResponseMessage Get([FromUri] Guid accountId)
        {
            HttpResponseMessage response;
            try
            {
                if (!ValidateFilters())
                    throw new AuthorizationException();

                // collect available account filters
                var filters = _filterManager.GetAccountFilters(accountId);

                var value = "This is a change for TeamCity pre-check build";
                var value2 = "This is a change for TeamCity pre-check build2";

                response = Request.CreateResponse(HttpStatusCode.OK, filters);
            }
            catch (Exception ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException, message = "" });
            }

            return response;
        }

        //[CacheOutput(ServerTimeSpan = 100, ClientTimeSpan = 0, NoCache = true, MustRevalidate = true, Private = true)]
        /// <summary>
        /// Gets all current user's available filters.
        /// </summary>
        /// <param name="options"></param>
        /// <returns></returns>
        [Route("")]
        public HttpResponseMessage Get([FromUri] FilterOptions options)
        {
            options = options ?? FilterOptions.Default;

            HttpResponseMessage response;
            try
            {
                if (!ValidateFilters())
                    throw new AuthorizationException();

                var userName = this.GetCurrentUserName();
                var currentUser = _accountsRepository.GetUserByUsername(userName);
                if (currentUser == null)
                    throw new UnknownUserException(userName);

                var result = new List<FilterDto>();
                var commonUserId = Guid.Empty;

                // collect available filters ( owned + shared + standard)
                var filters = _filterManager.GetUserFilters(userName, options.Target);
                if (filters != null && filters.Any())
                {
                    filters = filters.Where( x => _accountsRepository.VerifyUserExists(x.OwnerUserId)).ToList();
                    commonUserId = currentUser.Id;

                    foreach (var filter in filters)
                    {
                        var filterDto = FilterDto.ToFilterDto(currentUser.Id, filter, options.WithExtendedData, _companyScreeningRepository.GetFilterCriteriasByFilterId(filter.Id).Count);

                        filterDto.AssociatedUserId = commonUserId;

                        result.Add(filterDto);
                    }
                }

                response = Request.CreateResponse(HttpStatusCode.OK, result);
            }
            catch (UnknownUserException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized,
                                                 new { ErrorStatusCode = WebAPIGeneralErrorCodes.UserNotFound, message = "Unknown user.", Details = options });
            }
            catch (AuthorizationException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized,
                                   new { ErrorStatusCode = WebAPIGeneralErrorCodes.AuthorizationFailed, message = "Not authorized.", Details = options });
            }
            catch (CustomException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError,
                                new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException, message = "", Details = options });
            }
            catch (Exception ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError,
                                new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException, message = "", Details = options });
            }

            return response;
        }

        [Route("{filterId}")]
        public HttpResponseMessage Get(Guid filterId, [FromUri] Guid accountId, Guid userId, bool includeAllDetails)
        {
            HttpResponseMessage response;

            try
            {
                var userName = this.GetCurrentUserName();
                if (userName == null)
                    throw CustomExceptionBuilder.CreateUserNotAuthenticated();

                var currentUser = _accountsRepository.GetUserByUsername(userName);
                if (currentUser == null)
                    throw new UnknownUserException(userName);

                var loadedFilter = _filterManager.LoadFilter(filterId, userName);

                if (loadedFilter == null)
                {
                    response = Request.CreateResponse(HttpStatusCode.NotFound, new { ErrorStatusCode = WebAPIGeneralErrorCodes.ResourceNotFound, resourceId = filterId });
                }
                else
                {
                    var returnDto = FilterDto.ToFilterDto(currentUser.Id, loadedFilter, includeAllDetails);
                    response = Request.CreateResponse(HttpStatusCode.OK, returnDto);
                }
            }
            catch (UnknownUserException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized,

                                                 new
                                                 {
                                                     ErrorStatusCode = WebAPIGeneralErrorCodes.UserNotFound,
                                                     Details = new { resourceId = filterId }
                                                 });
            }
            catch (AuthorizationException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized,
                                   new
                                   {
                                       ErrorStatusCode = WebAPIGeneralErrorCodes.AuthorizationFailed,
                                       Details = new { resourceId = filterId }
                                   });
            }
            catch (CustomException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError,
                                new
                                {
                                    ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException,
                                    Details = new { resourceId = filterId }
                                });
            }
            catch (Exception ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException });
            }

            return response;
        }

        [Route("")]
        [InvalidateCacheOutput("Get")]
        public HttpResponseMessage Patch([FromBody] FilterPatchOpDto filterCriteriaOrder)
        {
            var response = Request.CreateResponse(HttpStatusCode.InternalServerError);

            try
            {
                if (!ValidateFilters())
                    throw new AuthorizationException();

                if (filterCriteriaOrder == null)
                {
                    response = Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorStatusCode = WebAPIGeneralErrorCodes.InputIsNull, Details = "filter criteria order is null." });
                }
                else
                {
                    var userName = this.GetCurrentUserName();
                    if (userName == null)
                        throw CustomExceptionBuilder.CreateUserNotAuthenticated();

                    //Updates order of criterias in filter
                    if (filterCriteriaOrder.OrderedCriteriaIds != null)
                    {
                        try
                        {
                            _filterManager.UpdateSelectedCriteriaOrderNumber(filterCriteriaOrder.ResourceId, filterCriteriaOrder.OrderedCriteriaIds);
                            response = Request.CreateResponse(HttpStatusCode.NoContent);
                        }
                        catch (Exception ex)
                        {
                            response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException, message = "Error saving filter criteria order.", Details = new { postedObject = filterCriteriaOrder } });
                            return response;
                        }
                    }
                }
            }
            catch (AuthorizationException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized, new { ErrorStatusCode = WebAPIGeneralErrorCodes.AuthorizationFailed, Details = new { postedObject = filterCriteriaOrder } });
            }
            catch (UnknownUserException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UserNotFound, Details = new { postedObject = filterCriteriaOrder } });
            }
            catch (CustomException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException, Details = new { postedObject = filterCriteriaOrder } });
            }
            catch (Exception ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException });
            }

            return response;
        }

        [Route("customFilterOrder")]
        public HttpResponseMessage Patch([FromBody] UserCustomFilterOrderDto filterOrder)
        {
            var response = Request.CreateResponse(HttpStatusCode.InternalServerError);

            try
            {
                if (!ValidateFilters())
                    throw new AuthorizationException();

                if (filterOrder == null)
                {
                    response = Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorStatusCode = WebAPIGeneralErrorCodes.InputIsNull, Details = "filter order is null." });
                }
                else
                {
                    var userName = this.GetCurrentUserName();
                    if (userName == null)
                        throw CustomExceptionBuilder.CreateUserNotAuthenticated();

                    var currentUser = _accountsRepository.GetUserByUsername(userName);
                    if (currentUser == null)
                        throw new UnknownUserException(userName);

                    //save order of filters in UserSettings collection
                    if (filterOrder.FilterIds != null)
                    {
                        try
                        {
                            _filterManager.SaveUserCustomFilterOrder(currentUser.Id, currentUser.AccountId, filterOrder.TargetType, filterOrder.ScreenRegion, filterOrder.FilterIds);
                            response = Request.CreateResponse(HttpStatusCode.NoContent);
                        }
                        catch (Exception ex)
                        {
                            response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException, message = "Error saving filter order.", Details = new { postedObject = filterOrder } });
                            return response;
                        }
                    }
                }
            }
            catch (AuthorizationException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized, new { ErrorStatusCode = WebAPIGeneralErrorCodes.AuthorizationFailed, Details = new { postedObject = filterOrder } });
            }
            catch (UnknownUserException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UserNotFound, Details = new { postedObject = filterOrder } });
            }
            catch (CustomException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException, Details = new { postedObject = filterOrder } });
            }
            catch (Exception ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException });
            }

            return response;
        }

        /// <summary>
        /// Create / Edit filter.
        /// </summary>
        /// <param name="filter"></param>
        /// <returns></returns>
        [Route("")]
        public HttpResponseMessage Post([FromBody] FilterAddEditDto filter)
        {
            HttpResponseMessage response;
            try
            {
                if (!ValidateFilters())
                    throw new AuthorizationException();

                var userName = this.GetCurrentUserName();
                var currentUser = _accountsRepository.GetUserByUsername(userName);

                if (filter == null)
                {
                    response = Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorStatusCode = WebAPIGeneralErrorCodes.InputIsNull, Details = "filter is null" });
                }
                else
                {
                    if (userName == null)
                        throw CustomExceptionBuilder.CreateUserNotAuthenticated();

                    if (currentUser == null)
                        throw new UnknownUserException(userName);

                    var result = _filterManager.SaveFilter(userName, filter);

                    response = Request.CreateResponse(HttpStatusCode.OK, result);
                }
            }
            catch (AuthorizationException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized, new { ErrorCode = WebAPIGeneralErrorCodes.AuthorizationFailed, message = "" });
            }
            catch (FilterValidationException validationException)
            {
                var errorCode = 0;
                var message = "";
                switch (validationException.InternalCode)
                {
                    case FilterValidationErrorCodes.DuplicatePrivateName:
                        errorCode = (int)ErrorCodes.ScreeningTool_FilterDuplicatePrivateName;
                        message = "You already have a private filter with the same name.";
                        break;

                    case FilterValidationErrorCodes.DuplicateSharedName:
                        errorCode = (int)ErrorCodes.ScreeningTool_FilterDuplicateSharedName;
                        message = "You already have a shared filter with the same name within the account.";
                        break;

                    case FilterValidationErrorCodes.NullOrEmptyName:
                        errorCode = (int)ErrorCodes.ScreeningTool_FilterEmptyName;
                        message = "Please assign a valid name.";
                        break;

                    default:
                        break;
                }

                response = Request.CreateResponse(HttpStatusCode.Conflict, new { ErrorCode = errorCode, message = message });
            }
            catch (UnknownUserException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized, new { ErrorCode = WebAPIGeneralErrorCodes.UserNotFound, message = "Unknown user." });
            }
            catch (CustomException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorCode = WebAPIGeneralErrorCodes.UnhandledException });
            }
            catch (Exception ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorCode = WebAPIGeneralErrorCodes.UnhandledException });
            }

            return response;
        }

        [Route("")]
        public HttpResponseMessage Put([FromBody]FilterDto filter)
        {
            HttpResponseMessage response;
            try
            {
                if (!ValidateFilters())
                    throw new AuthorizationException();

                if (filter == null)
                {
                    response = Request.CreateResponse(HttpStatusCode.BadRequest, new { ErrorCode = WebAPIGeneralErrorCodes.InputIsNull, message = "filter is null" });
                }
                else
                {
                    var userName = this.GetCurrentUserName();
                    if (userName == null)
                        throw CustomExceptionBuilder.CreateUserNotAuthenticated();

                    var currentUser = _accountsRepository.GetUserByUsername(userName);
                    if (currentUser == null)
                        throw new UnknownUserException(userName);

                    //map FilterDto to Filter
                    Filter newFilter = new Filter
                    {
                        Id = filter.Id,
                        Name = filter.Name,
                        Type = filter.Type,
                        OwnerUserId = filter.OwnerUserId,
                        AccountId = filter.AccountId,
                        LastSaved = DateTime.Now.ToUniversalTime(),
                        CreatedAt = filter.CreatedAt,
                        IsFavorite = filter.IsFavorite,
                        Status = filter.Status,

                        PortfolioId = filter.PortfolioId,
                        PortfolioName = filter.PortfolioName,

                        UniverseId = filter.UniverseId,
                        UniverseName = filter.UniverseName,

                        PeerGroup = filter.PeerGroup,
                        PeerGroupName = filter.PeerGroupName,

                        Region = filter.Region,
                        RegionName = filter.RegionName,

                        Country = filter.Country,
                        CountryName = filter.CountryName,

                        CompanyType = filter.CompanyType,

                        //for Report
                        WeightMatrixId = filter.WeightMatrixId,
                        WeightMatrixName = filter.WeightMatrixName,

                        ReferenceUniverseId = filter.ReferenceUniverseId,
                        ReferenceUniverseName = filter.ReferenceUniverseName,

                        ComparativeGroup = filter.ComparativeGroup,

                        CompanyTypePublicOrPrivate = filter.CompanyTypePublicOrPrivate,
                        ResearchType = filter.ResearchType,

                        TargetType = filter.TargetType,
                        ExportType = filter.ExportType
                    };

                    _filterManager.EditFilter(userName, newFilter);

                    response = Request.CreateResponse(HttpStatusCode.OK, newFilter);
                }
            }
            catch (AuthorizationException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized, new { ErrorCode = WebAPIGeneralErrorCodes.AuthorizationFailed, message = "" });
            }
            catch (FilterValidationException validationException)
            {
                var errorCode = 0;
                var message = "";
                switch (validationException.InternalCode)
                {
                    case FilterValidationErrorCodes.DuplicatePrivateName:
                        errorCode = (int)ErrorCodes.ScreeningTool_FilterDuplicatePrivateName;
                        message = "You already have a private filter with the same name.";
                        break;

                    case FilterValidationErrorCodes.DuplicateSharedName:
                        errorCode = (int)ErrorCodes.ScreeningTool_FilterDuplicateSharedName;
                        message = "You already have a shared filter with the same name within the account.";
                        break;

                    case FilterValidationErrorCodes.NullOrEmptyName:
                        errorCode = (int)ErrorCodes.ScreeningTool_FilterEmptyName;
                        message = "Please assign a valid name.";
                        break;

                    default:
                        break;
                }

                response = Request.CreateResponse(HttpStatusCode.Conflict, new { ErrorCode = errorCode, message = message });
            }
            catch (UnknownUserException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.Unauthorized, new { ErrorCode = WebAPIGeneralErrorCodes.UserNotFound, message = "Unknown user." });
            }
            catch (CustomException ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorCode = WebAPIGeneralErrorCodes.UnhandledException });
            }
            catch (Exception ex)
            {
                response = Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorCode = WebAPIGeneralErrorCodes.UnhandledException });
            }

            return response;
        }

        [Route("{id}")]
        public HttpResponseMessage Delete(Guid id)
        {
            try
            {
                var userName = this.GetCurrentUserName();
                _filterManager.DeleteFullFilter(id, userName);
                return Request.CreateResponse(HttpStatusCode.NoContent);
            }
            catch (NotAllowedToDeleteFilter ex)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { ErrorStatusCode = WebAPIGeneralErrorCodes.UnhandledException });
            }
            catch (Exception)
            {
                return Request.CreateResponse(HttpStatusCode.InternalServerError, new { errorCode = 100, message = "Error removing filter." });
            }
        }

        private bool ValidateFilters()
        {
            return _claimsChecker.ValidateAnyShortName(this);
        }
    }
}
